/*
 *	DEMO HELPERS
 */


/**
 *    debugData
 *
 *    Pass me a data structure {} and I'll output all the key/value pairs - recursively
 *
 *    @example var HTML = debugData( oElem.style, "Element.style", { keys: "top,left,width,height", recurse: true, sort: true, display: true, returnHTML: true });
 *
 *    @param Object    o_Data   A JSON-style data structure
 *    @param String    s_Title  Title for dialog (optional)
 *    @param Hash    options  Pass additional options in a hash
 */
function debugData(o_Data, s_Title, options) {
    options = options || {};
    var
        str = (s_Title || s_Title === '' ? s_Title : 'DATA')
        //	maintain backward compatibility with OLD 'recurseData' param
        , recurse = (typeof options == 'boolean' ? options : options.recurse !== false)
        , keys = (options.keys ? ',' + options.keys + ',' : false)
        , display = options.display !== false
        , html = options.returnHTML !== false
        , sort = !!options.sort
        , prefix = options.indent ? '    ' : ''
        , D = [], i = 0 // Array to hold data, i=counter
        , hasSubKeys = false
        , k, t, skip, x, type    // loop vars
        ;
    if (!o_Data || typeof o_Data !== 'object') {
        if (options.display) alert((s_Title || 'debugData') + ': ' + o_Data);
        return o_Data;
    }
    if (o_Data.jquery) {
        str = s_Title + 'jQuery Collection (' + o_Data.length + ')\n    context="' + o_Data.context + '"';
    }
    else if (o_Data.tagName && typeof o_Data.style == 'object') {
        str = s_Title + o_Data.tagName;
        var id = o_Data.id, cls = o_Data.className, src = o_Data.src, hrf = o_Data.href;
        if (id)  str += '\n    id="' + id + '"';
        if (cls) str += '\n    class="' + cls + '"';
        if (src) str += '\n    src="' + src + '"';
        if (hrf) str += '\n    href="' + hrf + '"';
    }
    else {
        parse(o_Data, prefix); // recursive parsing
        if (sort && !hasSubKeys) D.sort(); // sort by keyName - but NOT if has subKeys!
        if (str) str += '\n***' + '****************************'.substr(0, str.length) + '\n';
        str += D.join('\n'); // add line-breaks
    }

    if (display) alert(str); // display data
    if (html) str = str.replace(/\n/g, ' <br>').replace(/  /g, ' &nbsp;'); // format as HTML
    return str;

    function parse(data, prefix) {
        if (typeof prefix == 'undefined') prefix = '';
        try {
            $.each(data, function (key, val) {
                k = prefix + key + ':  ';
                skip = (keys && keys.indexOf(',' + key + ',') === -1);
                type = $.type(val);
                if (type === "date" || type === "regexp") {
                    val = val.toString();
                    type = "string";
                }
                if (type === "string") {            // STRING
                    if (!skip) D[i++] = k + '"' + val + '"';
                }
                // NULL, UNDEFINED, NUMBER or BOOLEAN
                else if (type === "null" || type === "undefined" || type === "number" || type === "boolean") {
                    if (!skip) D[i++] = k + val;
                }
                else if (type === "function") {    // FUNCTION
                    if (!skip) D[i++] = k + 'function()';
                }
                else if (val.jquery) {            // JQUERY OBJECT
                    if (!skip) D[i++] = k + 'jQuery (' + val.length + ') context="' + val.context + '"';
                }
                else if (val.nodeName) {        // DOM ELEMENT
                    var id = val.id, cls = val.className, src = val.src, hrf = val.href;
                    if (skip) D[i++] = k + ' ' +
                        id ? 'id="' + id + '"' :
                        src ? 'src="' + src + '"' :
                            hrf ? 'href="' + hrf + '"' :
                                cls ? 'class="' + cls + '"' :
                                    '';
                }
                else if (type === "array") {        // ARRAY
                    if (!skip) {
                        D[i++] = k + '[';
                        parse(val, prefix + '    '); // RECURSE
                        D[i++] = prefix + ']';
                        /*
                         if (val.length && typeof val[0] == "object") { // array of objects (hashs or arrays)
                         D[i++] = k +'[';
                         parse( val, prefix+'    '); // RECURSE
                         D[i++] = prefix +']';
                         }
                         else
                         D[i++] = k +'[ '+ val.toString() +' ]'; // output delimited array
                         */
                    }
                }
                else {                            // OBJECT or JSON
                    // TODO: why are some JSON keys not $.isSimpleObject
                    if (false && !$.isSimpleObject) {
                        if (!skip) D[i++] = k + 'OBJECT';
                    }
                    else if (!recurse || $.isEmptyObject(val)) { // show an empty hash
                        if (!skip) D[i++] = k + '{ }';
                    }
                    else { // recurse into JSON hash - indent output
                        D[i++] = k + '{';
                        parse(val, prefix + '    '); // RECURSE
                        D[i++] = prefix + '}';
                    }
                }
            });
        } catch (e) {
        }
    }
}
;

function debugStackTrace(s_Title, options) {
    var
        callstack = []
        , isCallstackPopulated = false
        ;
    try {
        i.dont.exist += 0; // doesn't exist- that's the point
    } catch (e) {
        if (e.stack) { // Firefox
            var lines = e.stack.split('\n');
            for (var i = 0, len = lines.length; i < len; i++) {
                if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
                    callstack.push(lines[i]);
                }
            }
            //Remove call to printStackTrace()
            callstack.shift();
            isCallstackPopulated = true;
        }
        else if (window.opera && e.message) { // Opera
            var lines = e.message.split('\n');
            for (var i = 0, len = lines.length; i < len; i++) {
                if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
                    var entry = lines[i];
                    //Append next line also since it has the file info
                    if (lines[i + 1]) {
                        entry += ' at ' + lines[i + 1];
                        i++;
                    }
                    callstack.push(entry);
                }
            }
            //Remove call to printStackTrace()
            callstack.shift();
            isCallstackPopulated = true;
        }
    }

    if (!isCallstackPopulated) { // IE and Safari
        var currentFunction = arguments.callee.caller;
        while (currentFunction) {
            var fn = currentFunction.toString();
            var fname = fn.substring(fn.indexOf('function') + 8, fn.indexOf('')) || 'anonymous';
            callstack.push(fname);
            currentFunction = currentFunction.caller;
        }
    }

    debugData(callstack, s_Title, options);
}
;

if (!window.console) window.console = { log:debugData };

if (!window.console.trace)
    window.console.trace = function (s_Title) {
        window.console.log(debugStackTrace(s_Title, { display:false, returnHTML:false, sort:false }));
    };

// add method to output 'hash data' inside an string
window.console.data = function (data, title) {
    var w = { array:['[', ']'], object:['{', '}'], string:['"', '"'], number:['', ''], 'function':['', '()'] }
        , x = $.type(data)
        , obj = x.match(/(object|array)/)
        , delim = !obj ? ['', ''] : x === 'object' ? ['{\n', '\n}'] : ['[\n', '\n]']
        , opts = { display:false, returnHTML:false, sort:false, indent:!!obj }
        , debug = debugData(data, '', opts)
        ;
    console.log(
        (title ? title + ' = ' : '')
            + delim[0]
            + ($.type(debug) === 'string' ? debug.replace(/    /g, '\t') : debug)
            + delim[1]
    );
};


/**
 *    timer
 *
 *    Utility for debug timing of events
 *    Can track multiple timers and returns either a total time or interval from last event
 *    @param String    timerName    Name of the timer - defaults to debugTimer
 *    @param String    action        Keyword for action or return-value...
 *    action: 'reset' = reset; 'clear' = delete; 'total' = ms since init; 'step' or '' = ms since last event
 */
/**
 *    timer
 *
 *    Utility method for timing performance
 *    Can track multiple timers and returns either a total time or interval from last event
 *
 *    returns time-data: {
 *        start:    Date Object
 *    ,    last:    Date Object
 *     ,    step:    99 // time since 'last'
 *    ,    total:    99 // time since 'start'
 *    }
 *
 *    USAGE SAMPLES
 *    =============
 *    timer('name'); // create/init timer
 *    timer('name', 'reset'); // re-init timer
 *    timer('name', 'clear'); // clear/remove timer
 *    var i = timer('name');  // how long since last timer request?
 *    var i = timer('name', 'total'); // how long since timer started?
 *
 *    @param String    timerName    Name of the timer - defaults to debugTimer
 *    @param String    action        Keyword for action or return-value...
 *    @param Hash        options        Options to customize return data
 *    action: 'reset' = reset; 'clear' = delete; 'total' = ms since init; 'step' or '' = ms since last event
 */
function timer(timerName, action, options) {
    var
        name = timerName || 'debugTimer'
        , Timer = window[ name ]
        , defaults = {
        returnString:true, padNumbers:true, timePrefix:'', timeSuffix:''
    }
        ;

    // init the timer first time called
    if (!Timer || action == 'reset') { // init timer
        Timer = window[ name ] = {
            start:new Date(), last:new Date(), step:0 // time since 'last', total:0 // time since 'start', options:$.extend({}, defaults, options)
        };
    }
    else if (action == 'clear') { // remove timer
        window[ name ] = null;
        return null;
    }
    else { // update existing timer
        Timer.step = (new Date()) - Timer.last;  // time since 'last'
        Timer.total = (new Date()) - Timer.start; // time since 'start'
        Timer.last = new Date();
    }

    var
        time = (action == 'total') ? Timer.total : Timer.step
        , o = Timer.options // alias
        ;

    if (o.returnString) {
        time += ""; // convert integer to string
        // pad time to 4 chars with underscores
        if (o.padNumbers)
            switch (time.length) {
                case 1:
                    time = "&ensp;&ensp;&ensp;" + time;
                    break;
                case 2:
                    time = "&ensp;&ensp;" + time;
                    break;
                case 3:
                    time = "&ensp;" + time;
                    break;
            }
        // add prefix and suffix
        if (o.timePrefix || o.timeSuffix)
            time = o.timePrefix + time + o.timeSuffix;
    }

    return time;
}
;


/**
 *    showOptions
 *
 *    Pass a layout-options object, and the pane/key you want to display
 */
function showOptions(Layout, key, debugOpts) {
    var data = Layout.options;
    $.each(key.split("."), function () {
        data = data[this]; // recurse through multiple key-levels
    });
    debugData(data, 'options.' + key, debugOpts);
}
;

/**
 *    showState
 *
 *    Pass a layout-options object, and the pane/key you want to display
 */
function showState(Layout, key, debugOpts) {
    var data = Layout.state;
    $.each(key.split("."), function () {
        data = data[this]; // recurse through multiple key-levels
    });
    debugData(data, 'state.' + key, debugOpts);
}
;


/**
 *    addThemeSwitcher
 *
 *    Remove the cookie set by the UI Themeswitcher to reset a page to default styles
 *
 *    Dependancies: /lib/js/themeswitchertool.js
 */
function addThemeSwitcher(container, position) {
    var pos = { top:'10px', right:'10px', zIndex:10 };
    $('<div id="themeContainer" style="position: absolute; overflow-x: hidden;"></div>')
        .css($.extend(pos, position))
        .appendTo(container || 'body')
        .themeswitcher()
    ;
}
;

/**
 *    removeUITheme
 *
 *    Remove the cookie set by the UI Themeswitcher to reset a page to default styles
 */
function removeUITheme(cookieName, removeCookie) {
    $('link.ui-theme').remove();
    $('.jquery-ui-themeswitcher-title').text('Switch Theme');
    if (removeCookie !== false)
        $.cookie(cookieName || 'jquery-ui-theme', null);
}
;


function debugWindow(content, options) {
    var defaults = {
        css:{
            position:'fixed', top:0
        }
    };
    $.extend(true, (options || {}), defaults);
    var $W = $('<div></div>')
        .html(content.replace(/\n/g, '<br>').replace(/  /g, ' &nbsp;'))// format as HTML
        .css(options.css)
        ;
}
;

